package com.zh.mycommon.viewdata;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 将查询父子级数据拼成层次形
 * 注意：必须满足根级数据的parentID=0或""
 */
public final class TreeDataUtil {
    private TreeDataUtil() {
    }


    /**
     * 性能更快
     * @param collect
     * @param root
     * @return
     * @param <T>
     */
    @SuppressWarnings("unchecked")
    public static <T extends TreeData<?>> List<T> treeDataFast(Collection<T> collect, T root) {
        if (collect.isEmpty()) return Collections.emptyList();
        fieldIdZero(root);

        buildChildren(collect, root);
        return (List<T>) root.getChildren();
    }

    /**
     * 更方便
     * @param collect
     * @return
     * @param <T>
     */
    @SuppressWarnings("unchecked")
    public static <T extends TreeData<?>> List<T> treeData(Collection<T> collect) {
        if (collect.isEmpty()) return Collections.emptyList();

        T root = null;
        Class<?> clazz = collect.stream().findAny().get().getClass();
        try {
            Constructor<?> constructor = clazz.getDeclaredConstructor();
            constructor.setAccessible(true);
            root = (T) constructor.newInstance();

        } catch (Exception ex) {
            throw new RuntimeException(ex.getCause());
        }
        fieldIdZero(root); //ID赋零值

        jointChildren(new ArrayList<>(collect), root);

        return (List<T>) root.getChildren();
    }

    // ID置零
    private static <T extends TreeData<?>> void fieldIdZero(T root) {
        Class<?> clazz = root.getClass();
        Class<?> actualType = null;
        //获取泛型 Id field 的的类型
//            Type[] genericInterfaces = ArrayUtils.addAll(clazz.getGenericInterfaces(), clazz.getGenericSuperclass());
        for (Type type : clazz.getGenericInterfaces()) {
            if (type instanceof ParameterizedType parameterType && parameterType.getRawType() == TreeData.class) {
                actualType = (Class<?>) parameterType.getActualTypeArguments()[0];
                break;
            }
        }
        if (actualType == null) {
            throw new IllegalArgumentException("未找到TreeData的泛型");
        }

        try {
            Method setId = clazz.getMethod("setId", Comparable.class);

            if (actualType.isAssignableFrom(String.class)) setId.invoke(root, "");
            else if (actualType.isAssignableFrom(Integer.class)) setId.invoke(root, 0);
            else if (actualType.isAssignableFrom(Long.class)) setId.invoke(root, 0L);
        } catch (Exception e) {
            throw new RuntimeException("id置零失败。cause: " + e.getMessage());
        }

    }

    /**
     * 构造节点的父子关系
     *
     * @param collect
     * @param root
     * @param <T>
     */
    @SuppressWarnings("unchecked")
    private static <T extends TreeData<?>> void buildChildren(Collection<T> collect, T root) {
        if (root.getChildren() == null) {
            root.addChildren(new ArrayList<>());
        }

        // 使用Map缓存父节点（如果标记了叶子节点，则不包含叶子节点）
        Map<Comparable<?>, T> nodeMap = new HashMap<>(collect.size());
        nodeMap.put(root.getId(), root); // 根节点放入Map

        for (T item : collect) {
            //为了让叶子节点的children=null;
            if (!item.isLeaf()) {
                item.addChildren(new ArrayList<>());
                nodeMap.put(item.getId(), item);
            }
        }

        // 第二遍循环：构建父子关系
        for (T item : collect) {
            T parent = nodeMap.get(item.getParentId());
            if (parent != null) {
                var children = parent.getChildren();
                assert children != null;
                ((List<T>) children).add(item);
            }
        }
        nodeMap.clear();
    }


    /**
     * 构造节点的父子关系（使用递归+list查找）
     *
     * @param collect
     * @param parent
     * @param <T>
     */
    @SuppressWarnings("unchecked")
    private static <T extends TreeData<?>> void jointChildren(Collection<T> collect, T parent) {
        if (parent.isLeaf())
            return;  //如果标记了叶子节点，则叶子节点直接return

        //当list为空时，让没有子节点的元素设置空List
        if (collect.isEmpty()) {
            parent.addChildren(Collections.emptyList());
            return;
        }

        var pid = parent.getId();    //父级ID

        List<T> list = new ArrayList<>();
        //循环
        collect.removeIf(e -> Objects.equals(e.getParentId(), pid) && list.add(e));
        parent.addChildren(list);

        //循环递归
        for (T node : list) {
            jointChildren(collect, node);
        }
    }


}
